// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using BuildXL.Pips.DirectedGraph;

namespace BuildXL.Execution.Analyzer.Analyzers.Simulator
{
    internal struct PipSpan
    {
        public NodeId Id;
        public int Thread;
        public ulong StartTime;
        public ulong Duration;

        public ulong EndTime
        {
            get
            {
                return StartTime + Duration;
            }
        }
    }

    internal struct PipAndPriority : IComparable<PipAndPriority>
    {
        public NodeId Node;
        public ulong Priority;

        public int CompareTo(PipAndPriority other)
        {
            int result = Priority.CompareTo(other.Priority);
            if (result != 0)
            {
                return result;
            }

            return Node.Value.CompareTo(other.Node.Value);
        }
    }

    /// <summary>
    /// Chains compares to perform comparison operations.
    /// </summary>
    public class ComparerBuilder<T> : Comparer<T>
    {
        private readonly LinkedList<IComparer<T>> comparers = new LinkedList<IComparer<T>>();

        private static readonly bool s_isNullable = typeof(T).IsClass;

        /// <summary>
        /// Modifies the comparer so that it next compares items using specified comparer on the key generated by the function.
        /// </summary>
        /// <typeparam name="TCompare">the type of key to compare</typeparam>
        /// <param name="keySelector">function for selecting the comparison key</param>
        /// <param name="comparer">the comparer for the key</param>
        /// <returns>This comparer updated with this comparison at the end of the comparision chain</returns>
        public ComparerBuilder<T> CompareByAfter<TCompare>(Func<T, TCompare> keySelector, IComparer<TCompare> comparer = null)
        {
            comparer = comparer ?? Comparer<TCompare>.Default;
            comparers.AddLast(new SelectorComparer<TCompare>(keySelector, comparer));
            return this;
        }

        /// <summary>
        /// Modifies the comparer so that it begins by comparing items using specified comparer on the key generated by the function.
        /// </summary>
        /// <typeparam name="TCompare">the type of key to compare</typeparam>
        /// <param name="keySelector">function for selecting the comparison key</param>
        /// <param name="comparer">the comparer for the key</param>
        /// <returns>This comparer updated with this comparison at the beginning of the comparision chain</returns>
        public ComparerBuilder<T> CompareByBefore<TCompare>(Func<T, TCompare> keySelector, IComparer<TCompare> comparer = null)
        {
            comparer = comparer ?? Comparer<TCompare>.Default;
            comparers.AddFirst(new SelectorComparer<TCompare>(keySelector, comparer));
            return this;
        }

        private class SelectorComparer<TCompare> : Comparer<T>
        {
            private readonly Func<T, TCompare> selector;
            private readonly IComparer<TCompare> comparer;

            public SelectorComparer(Func<T, TCompare> selector, IComparer<TCompare> comparer)
            {
                this.selector = selector;
                this.comparer = comparer;
            }

            public override int Compare(T x, T y)
            {
                return comparer.Compare(selector(x), selector(y));
            }
        }

        /// <summary>
        /// Compares the items using the list of stored comparers. The result is the first nonzero value returned
        /// by a comparer in the comparison chain. If none of the comparers returns, a non-zero value, the result is
        /// zero.
        /// </summary>
        /// <param name="x">the first object to compare</param>
        /// <param name="y">the second object to compare</param>
        /// <returns>
        /// A signed integer that indicates the relative values of x and y, as shown
        /// in the following table.Value Meaning Less than zero, x is less than y.Zero, x
        /// equals y.Greater than zero, x is greater than y.</returns>
        public override int Compare(T x, T y)
        {
            if (s_isNullable)
            {
                if (object.ReferenceEquals(x, y))
                {
                    return 0;
                }

                if (x == null)
                {
                    return y == null ? 0 : -1;
                }

                if (y == null)
                {
                    return 1;
                }
            }

            var result = 0;
            foreach (var comparer in comparers)
            {
                result = comparer.Compare(x, y);
                if (result != 0)
                {
                    return result;
                }
            }

            return result;
        }
    }
}
